package com.jemframework.domain;

import java.io.Serializable;

import javax.persistence.EntityManager;

import com.jemframework.Context;
import com.jemframework.domain.exception.EntityNotFoundException;

public abstract class AbstractEntityHome<E extends Entity, ID extends Serializable> implements EntityHome<E, ID> {

  protected void generateIdentifierIfNecessary(EntityManager em, E anEntity) {
    IdentifierGeneratorFactory theFactory = Context.getInstance().getIdentifierGeneratorFactory();
    IdentifierGenerator theGenerator = theFactory.getGenerator(anEntity.getEntityType());
    if (theGenerator != null) {
      theGenerator.generateIdentifier(em, anEntity);
    }
  }

  protected abstract E createEntityInternal();

  protected abstract E createEntityInternal(ID anIdentifier);

  public E createEntity(EntityManager em) {
    E theEntity = createEntityInternal();
    applyDefaultValues(theEntity);
    return theEntity;
  }

  public E createEntity(EntityManager em, ID id) {
    E theEntity = createEntityInternal(id);
    applyDefaultValues(theEntity);
    return theEntity;
  }

  protected void applyDefaultValues(E anEntity) {
    // do nothing by default.
  }

  public void insertEntity(EntityManager em, E anEntity) {

    AbstractEntity theEntity = (AbstractEntity) anEntity;

    // Generate dependent identifiers if there are any.
    generateIdentifierIfNecessary(em, anEntity);

    // Update dependent entities.
    theEntity.updateDependentEntitiesBeforeInsert();

    // Persist the entity.
    em.persist(anEntity);
  }

  public void updateEntity(EntityManager em, E anEntity) {

    AbstractEntity theEntity = (AbstractEntity) anEntity;

    // Update dependent entities.
    theEntity.updateDependentEntitiesBeforeInsert();

    // Update the entity.
    em.merge(anEntity);
  }

  public void removeEntity(EntityManager em, E anEntity) {
    em.remove(anEntity);
  }

  public EntityFactory getEntityFactory() {
    return Context.getInstance().getEntityFactory();
  }

  /**
   * Tries to find entity by its identifier. Returns the entity if found, throws
   * {@link EntityNotFoundException} otherwise.
   * 
   * @throws EntityNotFoundException -
   *           if no entity with the specified identifier exists.
   * @return a NON-null value.
   */
  public abstract E findEntity(EntityManager em, ID aIdGenId) throws EntityNotFoundException;
}
